﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;

namespace Pixysoft.Tools
{
    public class AssemblyHelper
    {
        List<string> resolvelist = new List<string>();

        private static volatile AssemblyHelper instance;

        private static object syncRoot = new Object();

        public static AssemblyHelper Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        instance = new AssemblyHelper();
                    }
                }

                return instance;

            }
        }

        private AssemblyHelper()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            string currentpath = StringFilter.GetFilePath(Assembly.GetExecutingAssembly().Location);

            resolvelist.Insert(0, currentpath);
        }

        /// <summary>
        /// 取得路径文件的版本号
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public Version GetVersion(string path)
        {
            try
            {
                if (string.IsNullOrEmpty(path))
                    return null;

                if (!File.Exists(path))
                    return null;

                byte[] bassembly = System.IO.File.ReadAllBytes(path);

                Assembly assembly = Assembly.Load(bassembly);

                AssemblyName assemblyName = assembly.GetName();

                Version version = assemblyName.Version;

                return version;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 取得自己的版本号
        /// </summary>
        /// <returns></returns>
        public Version GetVersion()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            return GetVersion(assembly);
        }

        /// <summary>
        /// 获取制定的版本
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public Version GetVersion(Assembly assembly)
        {
            AssemblyName assemblyName = assembly.GetName();
            Version version = assemblyName.Version;
            return version;
        }


        /// <summary>
        /// 加载程序集 同时制定程序集解析路径 如果已经加载了，现在再加载不同路径的相同程序集，会出现重复加载
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public Assembly AssemblyLoadFrom(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                return null;

            //记录搜索路径

            string filepath = StringFilter.GetFilePath(filename);

            if (!resolvelist.Contains(filepath))
                resolvelist.Insert(0, filepath);

            Assembly target = null;

            try
            {
                target = Assembly.LoadFrom(filename);
            }
            catch (Exception ex)
            {
                return null;
            }

            return target;
        }

        /// <summary>
        /// 解决无法加载的程序集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            //arg name = pixysoft, version=xxx...

            if (Assembly.GetExecutingAssembly().FullName == args.Name)
                return Assembly.GetExecutingAssembly();

            Assembly referenceAssembly = null;

            foreach (string resolivepath in resolvelist)
            {
                string referenceAssemblyFilePreName = resolivepath + ParaseAssemblyName(args.Name);

                if (File.Exists(referenceAssemblyFilePreName + ".exe"))
                {
                    try
                    {
                        referenceAssembly = Assembly.LoadFrom(referenceAssemblyFilePreName + ".exe");
                    }
                    catch
                    {
                    }
                    break;
                }
                else if (File.Exists(referenceAssemblyFilePreName + ".dll"))
                {
                    try
                    {
                        referenceAssembly = Assembly.LoadFrom(referenceAssemblyFilePreName + ".dll");
                    }
                    catch
                    {
                    }
                    break;
                }
                else
                {
                    continue;
                }
            }

            return referenceAssembly;
        }

        private string ParaseAssemblyName(string name)
        {
            return name.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
        }

        /// <summary>
        /// 加载依赖项 使用递归
        /// </summary>
        /// <param name="assembly"></param>
        public bool LoadReference(Assembly target)
        {
            if (target == null)
                return false;

            List<string> referencelist = new List<string>();

            foreach (Assembly refer in AppDomain.CurrentDomain.GetAssemblies())
            {
                string assemblyname = refer.GetName().FullName;
                if (referencelist.Contains(assemblyname))
                    continue;
                referencelist.Add(assemblyname);
            }

            return LoadReference(referencelist, target);
        }

        /// <summary>
        /// 不重复加载程序集
        /// </summary>
        /// <param name="referencelist"></param>
        /// <param name="assembly"></param>
        /// <returns></returns>
        private bool LoadReference(List<string> referencelist, Assembly assembly)
        {
            foreach (AssemblyName referenceName in assembly.GetReferencedAssemblies())
            {
                string referenceAssemblyName = referenceName.FullName;

                if (referencelist.Contains(referenceAssemblyName))
                    continue;

                referencelist.Add(referenceAssemblyName);

                Assembly referenceAssembly = null;

                string referenceAssemblyFilePreName = StringFilter.GetFilePath(assembly.Location) + referenceName.Name;

                if (File.Exists(referenceAssemblyFilePreName + ".exe"))
                {
                    referenceAssembly = Assembly.LoadFrom(referenceAssemblyFilePreName + ".exe");
                }
                else if (File.Exists(referenceAssemblyFilePreName + ".dll"))
                {
                    referenceAssembly = Assembly.LoadFrom(referenceAssemblyFilePreName + ".dll");
                }
                else
                {
                    continue;
                }
                //try
                //{
                //    referenceAssembly = Assembly.Load(referenceName);
                //}
                //catch (Exception ex)
                //{
                //    continue;
                //}

                if (referenceAssembly == null)
                    return false;

                string referenceLocation = StringFilter.GetFilePath(referenceAssembly.Location);

                string assemblyLocation = StringFilter.GetFilePath(assembly.Location);

                string currentLocation = StringFilter.GetFilePath(Assembly.GetExecutingAssembly().Location);

                //防止加载系统类

                if (!string.Equals(referenceLocation, assemblyLocation, StringComparison.OrdinalIgnoreCase) &&
                    !string.Equals(referenceLocation, currentLocation, StringComparison.OrdinalIgnoreCase))
                    continue;

                if (!LoadReference(referencelist, referenceAssembly))
                    return false;
            }

            return true;
        }

        /// <summary>
        /// 获取引用 内存加载
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public List<AssemblyName> GetReferences(string path)
        {
            List<AssemblyName> list = new List<AssemblyName>();

            if (string.IsNullOrEmpty(path))
                return list;

            if (!File.Exists(path))
                return list;

            byte[] assembly = System.IO.File.ReadAllBytes(path);

            return GetReferences(Assembly.Load(assembly));
        }

        /// <summary>
        /// 取得这个assembly的依赖项
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public List<AssemblyName> GetReferences(Assembly assembly)
        {
            List<AssemblyName> list = new List<AssemblyName>();

            System.Reflection.AssemblyName[] ExesAndDlls = assembly.GetReferencedAssemblies();

            list.AddRange(ExesAndDlls);

            return list;
        }

        /// <summary>
        /// 取得文件名
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public string GetAssemblyFileName(Assembly assembly)
        {
            return assembly.ManifestModule.Name;
        }

        /// <summary>
        /// 取得程序集名，不包含后缀
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public string GetAssemblyName(Assembly assembly)
        {
            return assembly.GetName().Name;
        }


        private void test()
        {
            Assembly assembly = AssemblyHelper.Instance.AssemblyLoadFrom(null);
        }
    }
}
